package com.kebabeaters.nakebab.client.ui.view.widget;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.gwtplatform.mvp.client.ViewImpl;
import com.kebabeaters.nakebab.client.bl.ElementDescription;
import com.kebabeaters.nakebab.client.bl.Family;
import com.kebabeaters.nakebab.client.event.CircleMouseOverEvent;
import com.kebabeaters.nakebab.client.presenter.widget.CircleLayoutPresenterWidget.Display;
import com.kebabeaters.nakebab.client.resources.ResourcesFactory;
import com.kebabeaters.nakebab.client.resources.client.KebabResources;
import com.kebabeaters.nakebab.client.ui.widget.CircleLayoutPanel;
import com.kebabeaters.nakebab.client.ui.widget.CircleSubMenu;

public class CircleLayoutViewWidget extends ViewImpl implements Display {

	public static interface ImageUpdater {
		void onUpdate(double value);

		void setImage(Image image);
	}

	public interface Binder extends UiBinder<Widget, CircleLayoutViewWidget> {
	}

	private final Binder uiBinder = GWT.create(Binder.class);

	private Widget widget;

	private final static int ANIMATION_TIME = 3000;

	private CircleLayoutPanel<CircleSubMenu> clp;
	private List<Family<CircleSubMenu>> displayable;
	private CircleSubMenu selected;

	private CircleSubMenu aboutCircle;

	private CircleSubMenu usersTastersCircle;

	private CircleSubMenu usersProfileCircle;

	private CircleSubMenu restaurantAddCircle;

	private CircleSubMenu restaurantMapCircle;

	private CircleSubMenu restaurantSearchCircle;

	private CircleSubMenu rankTastersCircle;

	private CircleSubMenu rankUsersCircle;

	@UiField
	protected FlowPanel navigatorContainer;
	@UiField
	protected SimplePanel previewContainer;

	public CircleLayoutViewWidget() {

		widget = uiBinder.createAndBindUi(this);

		final int CATEGORY_RANGE = 120;
		final int SUBCATEGORY_RANGE = 60;
		final int CATEGORY_RADIUS_OFFSET = 0;
		final int SUBCATEGORY_RADIUS_OFFSET = 80;
		KebabResources resources = ResourcesFactory.getKebabResources();

		aboutCircle = initObject(15, CATEGORY_RANGE, CATEGORY_RADIUS_OFFSET,
				false);
		CircleSubMenu usersCircle = initObject(20, CATEGORY_RANGE,
				CATEGORY_RADIUS_OFFSET, false);
		CircleSubMenu restaurantCircle = initObject(25, CATEGORY_RANGE,
				CATEGORY_RADIUS_OFFSET, false);
		CircleSubMenu rankCircle = initObject(30, CATEGORY_RANGE,
				CATEGORY_RADIUS_OFFSET, false);

		usersTastersCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, 4);
		usersProfileCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, -12);

		restaurantAddCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, 2);
		restaurantMapCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 10, -2);
		restaurantSearchCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, -8);

		rankTastersCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, 0);
		rankUsersCircle = initObjectHack(15, SUBCATEGORY_RANGE,
				SUBCATEGORY_RADIUS_OFFSET, 8, -4);

		// children
		List<CircleSubMenu> usersCircleChildren = Arrays.asList(
				usersTastersCircle, usersProfileCircle);
		List<CircleSubMenu> restaurantCircleChildren = Arrays.asList(
				restaurantAddCircle, restaurantMapCircle,
				restaurantSearchCircle);
		List<CircleSubMenu> rankCircleChildren = Arrays.asList(
				rankTastersCircle, rankUsersCircle);

		// setting children
		usersCircle.setChildren(usersCircleChildren);
		restaurantCircle.setChildren(restaurantCircleChildren);
		rankCircle.setChildren(rankCircleChildren);

		// defining main circles (categories)
		ElementDescription<CircleSubMenu> aboutFamilyMain = new ElementDescription<CircleSubMenu>(
				aboutCircle, new Image(resources.menuAbout().getSafeUri()),
				new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> usersCircleMain = new ElementDescription<CircleSubMenu>(
				usersCircle, new Image(resources.menuUsersAll().getSafeUri()),
				new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> restaurantCircleMain = new ElementDescription<CircleSubMenu>(
				restaurantCircle, new Image(resources.menuRestaurantsAll()
						.getSafeUri()), new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> rankCircleMain = new ElementDescription<CircleSubMenu>(
				rankCircle, new Image(resources.menuRanksAll().getSafeUri()),
				new Image(resources.tasterRankingPreview().getSafeUri()));

		// defining user elements' descriptions
		ElementDescription<CircleSubMenu> usersProfileDescription = new ElementDescription<CircleSubMenu>(
				usersProfileCircle, new Image(resources.menuUsersProfile()
						.getSafeUri()), new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> usersTastersDescription = new ElementDescription<CircleSubMenu>(
				usersTastersCircle, new Image(resources.menuUsersTasters()
						.getSafeUri()), new Image(resources.inBuild().getSafeUri()));

		// definig restaurant elements' descriptions
		ElementDescription<CircleSubMenu> restaurantAddDescription = new ElementDescription<CircleSubMenu>(
				restaurantAddCircle, new Image(resources.menuRestaurantsAdd()
						.getSafeUri()),  new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> restaurantMapDescription = new ElementDescription<CircleSubMenu>(
				restaurantMapCircle, new Image(resources.menuRestaurantsMap()
						.getSafeUri()),  new Image(resources.inBuild().getSafeUri()));
		ElementDescription<CircleSubMenu> restaurantSearchDescription = new ElementDescription<CircleSubMenu>(
				restaurantSearchCircle, new Image(resources
						.menuRestaurantsSearch().getSafeUri()),  new Image(resources.inBuild().getSafeUri()));

		// defining rank elements' descriptions
		ElementDescription<CircleSubMenu> rankTastersDescription = new ElementDescription<CircleSubMenu>(
				rankTastersCircle, new Image(resources.menuRanksTasters()
						.getSafeUri()), new Image(resources.tasterRankingPreview().getSafeUri()));
		ElementDescription<CircleSubMenu> rankUsersDescription = new ElementDescription<CircleSubMenu>(
				rankUsersCircle, new Image(resources.menuRanksUsers()
						.getSafeUri()), new Image(resources.userRankingPreview().getSafeUri()));

		// defining sub-families
		List<ElementDescription<CircleSubMenu>> usersFamilySubcircle = Arrays
				.asList(usersProfileDescription, usersTastersDescription);
		List<ElementDescription<CircleSubMenu>> restaurantFamilySubcircle = Arrays
				.asList(restaurantAddDescription, restaurantMapDescription,
						restaurantSearchDescription);
		List<ElementDescription<CircleSubMenu>> rankFamilySubcircle = Arrays
				.asList(rankTastersDescription, rankUsersDescription);

		// defining families
		Family<CircleSubMenu> aboutFamily = new Family<CircleSubMenu>(
				aboutFamilyMain, null);
		Family<CircleSubMenu> usersFamily = new Family<CircleSubMenu>(
				usersCircleMain, usersFamilySubcircle);
		Family<CircleSubMenu> restaurantFamily = new Family<CircleSubMenu>(
				restaurantCircleMain, restaurantFamilySubcircle);
		Family<CircleSubMenu> rankFamily = new Family<CircleSubMenu>(
				rankCircleMain, rankFamilySubcircle);

		displayable = new ArrayList<Family<CircleSubMenu>>();
		displayable.add(rankFamily);
		displayable.add(restaurantFamily);
		displayable.add(usersFamily);
		displayable.add(aboutFamily);

		clp = new CircleLayoutPanel<CircleSubMenu>(150, 140, -50);
		clp.setCircleLeftTop(120.0, 130.0);
		clp.setChildren(Arrays.asList(aboutCircle, usersCircle,
				restaurantCircle, rankCircle));
		clp.expandChildrenNow();

		clp.setAnimationUpdater(new ImageUpdater() {

			private Image image;

			@Override
			public void onUpdate(double value) {

				if (image != null) {

					double position;

					if (value < 0.1) {
						position = 10 * image.getWidth() * value
								- image.getWidth();
					} else if (value > 0.9) {
						position = 10 * image.getWidth() * value - 9
								* image.getWidth();
					} else
						position = 0;

					image.getElement().getStyle().setLeft(position, Unit.PX);
				}
			}

			@Override
			public void setImage(Image image) {
				this.image = image;

				if (image != null) {
					image.getElement().getStyle()
							.setPosition(Position.ABSOLUTE);
					previewContainer.setWidget(image);
					image.setWidth("200px");
					image.setHeight("200px");
					image.getElement().getStyle().setLeft(-300, Unit.PX);
				}
			}
		});

		// clp.setWidth("500px");
		// clp.setHeight("500px");

		clp.enableAnimation(ANIMATION_TIME, displayable);

		navigatorContainer.add(clp);
	}

	@Override
	public Widget asWidget() {
		return widget;
	}

	private CircleSubMenu initObject(int size, int range, int radiusOffset,
			boolean subcategory) {
		final CircleSubMenu csm = new CircleSubMenu(size, range, radiusOffset);
		if (subcategory)
			csm.setCircleLeftBottom0((double) size);
		else
			csm.setCircleLeftBottom((double) size, (double) size);
		csm.setWidth(size + "px");
		csm.setHeight(size + "px");

		csm.addCircleMouseOverHandler(new CircleMouseOverEvent.Handler() {

			@Override
			public void onSelect(CircleMouseOverEvent event) {

				if (csm != selected) {

					if (selected != null)
						selected.hideChildren();

					if (event.isEntered()) {
						selected = csm;
						clp.disableAnimation();
						clp.setSelectedCircle(csm);
						csm.expandChildren();
						
						Image img = getPreviewImageFor(csm);
						if (img != null) {
							img.getElement().getStyle().setLeft(0, Unit.PX);
							img.setWidth("200px");
							img.setHeight("200px");
							previewContainer.setWidget(img);
						}
						
					} else {
						// clp.enableAnimation(ANIMATION_TIME, displayable);
						// csm.hideChildren();
						previewContainer.clear();
					}
				}
			}
		});

		return csm;
	}

	private CircleSubMenu initObjectHack(int size, int range, int radiusOffset,
			double left, double bottom) {
		final CircleSubMenu csm = new CircleSubMenu(size, range, radiusOffset);
		csm.setCircleLeftBottomSimple(left, bottom);
		csm.setWidth(size + "px");
		csm.setHeight(size + "px");

		csm.addCircleMouseOverHandler(new CircleMouseOverEvent.Handler() {

			@Override
			public void onSelect(CircleMouseOverEvent event) {

				if (event.isEntered()) {
					clp.disableAnimation();
					clp.setSelectedCircle(csm);

					Image img = getPreviewImageFor(csm);
					if (img != null) {
						img.setWidth("200px");
						img.setHeight("200px");
						previewContainer.setWidget(img);
					}

				} else {
					// clp.enableAnimation(ANIMATION_TIME, displayable);
					previewContainer.clear();
				}
			}
		});

		return csm;
	}

	private Image getPreviewImageFor(CircleSubMenu csm) {

		for (Family<CircleSubMenu> family : displayable) {
			if (family.getMainElement().getObj() == csm) {
				return family.getMainElement().getPreviewImage();
			} else {
				if (family.getChildren() != null)
					for (ElementDescription<CircleSubMenu> element : family
							.getChildren()) {
						if (element.getObj() == csm) {
							return element.getPreviewImage();
						}
					}
			}
		}

		return null;
	}

	@Override
	public HasClickHandlers menuRankUserOnClick() {
		return rankUsersCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuRankTasterOnClick() {
		return rankTastersCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuUserProfilOnClick() {
		return usersProfileCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuUserTastersOnClick() {
		return usersTastersCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuAboutOnClick() {
		return aboutCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuRestaurantAddOnClick() {
		return restaurantAddCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuRestaurantSearchOnClick() {
		return restaurantSearchCircle.getClickHandler();
	}

	@Override
	public HasClickHandlers menuRestaurantMapOnClick() {
		return restaurantMapCircle.getClickHandler();
	}

	@Override
	public void setAutoModeEnabled(boolean enabled) {
		clp.enableAnimation(ANIMATION_TIME, displayable);
	}

}
